package xcu.lxj.nettychat.user.service.Impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import xcu.lxj.nettychat.chat.domain.vo.response.WSBaseResp;
import xcu.lxj.nettychat.chat.service.adapter.WSAdapter;
import xcu.lxj.nettychat.common.config.ThreadPoolConfig;
import xcu.lxj.nettychat.user.dao.UserDao;
import xcu.lxj.nettychat.user.domain.entity.User;
import xcu.lxj.nettychat.user.service.inter.LoginService;
import xcu.lxj.nettychat.user.service.inter.WebSocketService;
import xcu.lxj.nettychat.websocket.netty.NettyAttrUtils;

import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @Author XinJian Lei
 * @Date 2024/9/27 17:30
 * @Version 1.0
 */
@Service
@Slf4j
public class WebSocketServiceImpl implements WebSocketService {

    /**
     * Tip
     * removed loginSuccess 还能写
     */



    /**
     * 所有在线的用户和对应的socket
     */
    private static final ConcurrentHashMap<Long, CopyOnWriteArrayList<Channel>> ONLINE_UID_MAP = new ConcurrentHashMap<>();

    @Autowired
    private LoginService loginService;
    @Autowired
    UserDao userDao;
    @Resource(name = ThreadPoolConfig.WS_EXECUTOR)
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Override
    public boolean authorize(Channel channel, String token) {
        //校验token
        boolean verifySuccess = loginService.verify(token);
        if (verifySuccess) {//用户校验成功给用户登录
            User user = userDao.getById(loginService.getValidUid(token));
            verifySuccess(channel, user, token);
        } else { //让前端的token失效
            sendMsg(channel, WSAdapter.buildWSInvalidateTokenResp());
            //关闭这个channel
            channel.close();
        }
        return verifySuccess;
    }

    private void verifySuccess(Channel channel, User user, String token) {
        log.info(" verifySuccess 校验成功 uid = {},成功连接" ,user.getId());
        CopyOnWriteArrayList<Channel> channels = Optional.ofNullable(ONLINE_UID_MAP.get(user.getId())).orElse(new CopyOnWriteArrayList<>());
        channels.add(channel);
        // 存入
        ONLINE_UID_MAP.put(user.getId(), channels);
        // 验证成功 再让每一个 channel 保存一下uid 方便以后处理
        NettyAttrUtils.setAttr(channel,NettyAttrUtils.UID,user.getId());
        // 以后可以 发送给对应好友 自己登陆成功了
        // setUserActiveStatus
        channel.writeAndFlush(new TextWebSocketFrame("你已经连接成功"));
    }

    /**
     * 关闭channel连接
     * @param channel
     */
    @Override
    public void removed(Channel channel) {

        Long uid = Optional.ofNullable(NettyAttrUtils.getAttr(channel, NettyAttrUtils.UID)).orElse(-1L);
        if(uid != -1){
            CopyOnWriteArrayList<Channel> channels = Optional.ofNullable(ONLINE_UID_MAP.get(uid)).orElse(new CopyOnWriteArrayList<>());
            // channel 不为空 移除这个channel
            if(CollectionUtil.isNotEmpty(channels) && channels.contains(channel)){
                channels.remove(channel);
//            if(channels.isEmpty()){
//                // 可以发送用户离线的 信息
//            }
            }
        }
        //关闭这个channel
        channel.close();
    }

    /**
     * 给对应用户发送消息
     * @param wsBaseResp ws基础响应体
     * @param uid  发送的uid
     */
    @Override
    public void sendToUid(WSBaseResp<?> wsBaseResp, Long uid) {
        CopyOnWriteArrayList<Channel> channels = ONLINE_UID_MAP.get(uid);
        if (CollectionUtil.isEmpty(channels)) {
            log.info(" sendToUid not online用户：{}不在线", uid);
            return;
        }
        channels.forEach(channel -> {
            threadPoolTaskExecutor.execute(() -> sendMsg(channel, wsBaseResp));
        });
    }

    @Override
    public void sendToAllOnline(WSBaseResp<?> wsBaseResp, Long skipUid) {
        ONLINE_UID_MAP.forEach((uid,channels)->{
            if(Objects.nonNull(skipUid) && Objects.equals(uid, skipUid)){
                return;
            }
            channels.forEach((channel -> {
                threadPoolTaskExecutor.execute(() -> sendMsg(channel, wsBaseResp));
            }));
        });
    }

    @Override
    public void sendToAllOnline(WSBaseResp<?> wsBaseResp) {
        sendToAllOnline(wsBaseResp, null);
    }

    /**
     * 通过channel 发送消息
     * @param channel
     * @param wsBaseResp
     */
    private void sendMsg(Channel channel, WSBaseResp<?> wsBaseResp) {
        channel.writeAndFlush(new TextWebSocketFrame(JSONUtil.toJsonStr(wsBaseResp)));
    }

}
